#include "bt_api.h"

/**
  * @brief 获取当前的RTC时间，并以字符串格式展示
  * @param datetime 存放日期和时间的字符数组
  * @retval None
  */
time_t RTC_getTimestamp ()
{
    RTC_DateTypeDef sdatestructureget;
    RTC_TimeTypeDef stimestructureget;

    /* 获取当前时间 */
    HAL_RTC_GetTime(&hrtc, &stimestructureget, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &sdatestructureget, RTC_FORMAT_BIN); // 必须紧跟在 HAL_RTC_GetTime 后面

    /* 构造 struct tm */
    struct tm timeinfo;
    timeinfo.tm_year = sdatestructureget.Year + 100;      // since 1900
    timeinfo.tm_mon  = sdatestructureget.Month - 1;       // 0-based
    timeinfo.tm_mday = sdatestructureget.Date;
    timeinfo.tm_hour = stimestructureget.Hours;
    timeinfo.tm_min  = stimestructureget.Minutes;
    timeinfo.tm_sec  = stimestructureget.Seconds;
    timeinfo.tm_isdst = 0;

    /* 转换为时间戳（本地时间 -> 秒） */
    time_t timestamp = mktime(&timeinfo);

    /* 减去8小时偏移，得到 UTC 时间戳 */
    return timestamp;
}

/**
  * @brief 处理收到的时间同步响应
  * @param code 响应代码
  * @param response 响应的JSON数据
  * @retval None
  */
static void handle_send_time_response(int code, cJSON* response)
{
	printf("handle_send_time_response\r\n");
	if(code == 200)
	{
		const cJSON* server_time = cJSON_GetObjectItem(response, "times");
		if(cJSON_IsString(server_time))
		{
			printf("收到服务器时间: %d\r\n", server_time->valuestring);

			// uniapp 给的是毫秒级时间戳，所以需要先除以 1000 得到秒
			time_t rawtime = atoi(server_time->valuestring) + 8 * 3600;

			struct tm *timeinfo = localtime(&rawtime);

			if (timeinfo != NULL)
			{
				RTC_TimeTypeDef sTime = {0};
				RTC_DateTypeDef sDate = {0};

				sTime.Hours = timeinfo->tm_hour;
				sTime.Minutes = timeinfo->tm_min;
				sTime.Seconds = timeinfo->tm_sec;
				sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
				sTime.StoreOperation = RTC_STOREOPERATION_RESET;

				sDate.WeekDay = timeinfo->tm_wday == 0 ? 7 : timeinfo->tm_wday;  // 周日是7
				sDate.Month = timeinfo->tm_mon + 1;   // 月份从0开始
				sDate.Date = timeinfo->tm_mday;
				sDate.Year = timeinfo->tm_year - 100; // HAL RTC从2000年算起

				// 必须先设置时间再设置日期
				if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
				{
					printf("设置RTC时间失败\r\n");
				}
				else if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
				{
					printf("设置RTC日期失败\r\n");
				}
				else
				{
					printf("RTC时间设置成功\r\n");
				}
			}
			else
			{
				printf("时间戳转换失败\r\n");
			}
		}
	}
	else
	{
		printf("同步时间失败，code = %d\n", code);
	}
}

/**
  * @brief 处理蓝牙心跳响应
  * @param code 响应代码
  * @param response 响应的JSON数据
  * @retval None
  */
static void handle_send_hs_response(int code, cJSON* response)
{
	printf("handle_send_hs_response\r\n");
    if(code == 200)
    {
        // 运行正常
    }
    else
    {
        // 连接正常，但连接有问题
    }
}

/**
  * @brief 命令和对应的处理函数映射
  * @param None
  * @retval send_command_info_t 数组，包含命令字符串和相应的处理函数
  */
typedef void (*send_cmd_handler_t)(int code, cJSON* response_data);
typedef struct {
    const char* command_str;  // 命令字符串
    send_cmd_handler_t handler;  // 命令处理函数
} send_command_info_t;

/**
  * @brief 定义命令和其对应的处理函数
  * @param None
  * @retval send_command_info_t 数组，包含命令字符串和相应的处理函数
  */
static send_command_info_t send_command_map[] = {
    { "BT+TIME", handle_send_time_response },
    { "BT+DC", NULL },
    { "BT+HS", handle_send_hs_response },
};

/**
  * @brief 解析蓝牙响应，调用对应的命令处理函数
  * @param frame_str 响应的JSON字符串
  * @param expected_callId 期望的callId
  * @param out_code 返回的响应代码
  * @param handler 处理响应的函数
  * @retval 若解析成功，返回1；否则返回0
  */
static _Bool parse_response(const char* frame_str, const char* expected_callId, int* out_code, send_cmd_handler_t handler)
{
	// 先查找帧头和帧尾
    const char* frame_start = strstr(frame_str, "@R@");
    const char* frame_end = strstr(frame_str, "@E@");
    if(!frame_start || !frame_end || frame_start > frame_end)
        return 0; // 帧格式错误

    frame_start += 3; // 跳过 @R@

    size_t json_len = frame_end - frame_start;
    if(json_len == 0)
        return 0; // 空数据

    // 拷贝出纯净JSON字符串
    char json_buf[256]; // 假设你的json不会超512字节
    if(json_len >= sizeof(json_buf))
        return 0; // 太大，防止越界

    memcpy(json_buf, frame_start, json_len);
    json_buf[json_len] = '\0'; // 补个字符串结束符
	printf("json:%s\r\n",json_buf);
    cJSON* root = cJSON_Parse(json_buf);
    if(!root) return 0;

    const cJSON* id = cJSON_GetObjectItem(root, "id");
    const cJSON* callId = cJSON_GetObjectItem(root, "callId");
    const cJSON* code = cJSON_GetObjectItem(root, "code");

    bt_info* info = bt_get_current_info();
    _Bool result = 0;

//    printf("%s\n%s\n%d\n", id->valuestring, callId->valuestring, code->valueint);
    if(cJSON_IsString(id) && cJSON_IsString(callId) && cJSON_IsNumber(code))
    {
        if(strcmp(id->valuestring, (const char*)info->hostId) == 0 && strcmp(callId->valuestring, expected_callId) == 0)
        {
            *out_code = code->valueint;
            if(handler && code->valueint == 200)
                handler(code->valueint, root);  // 调用对应的处理函数
            result = 1;
        }
    }

    cJSON_Delete(root);
    return result;
}

/**
  * @brief 发送蓝牙数据，带响应
  * @param data 要发送的数据
  * @param type 数据类型（如命令、数据、响应）
  * @param callId 调用ID
  * @param handler 响应处理函数
  * @retval 若发送成功并收到响应，返回1；否则返回0
  */
static _Bool send_ble_data(uint8_t* data, sendComType type, char* callId, send_cmd_handler_t handler)
{
    uint8_t res[RX_BUF_SIZE] = {0};
    size_t rxlen = 0;

    if(type == RES)
    {
        send_com_NoResp(data);  // 发送无响应数据
        return 1;
    }
    else if(type == DATA || type == COM)
    {
        int result = send_com_WithResp(data, (uint8_t*)"@R@", 20, 100, res, &rxlen);
        if(result == 1)
        {
			printf("RES:%s\r\n",res);
            int code = -1;
            res[rxlen] = '\0';

            if(parse_response((char*)res, callId, &code, handler))
            {
                return (code == 200);  // 如果响应代码是200，表示成功
            }
        }
        else if(result == -1)
        {
            bt_info* info = bt_get_current_info();
            info->connect_flag = 0;
            memset(info->hostId, 0, strlen((const char *)info->hostId));
        }
    }

    return 0;
}

/**
  * @brief 发送传感器数据
  * @param sensor_data 传感器数据
  * @retval 若发送成功，返回1；否则返回0
  */
_Bool send_sensor_data(SensorData* sensor_data)
{
    if(bt_get_current_info()->connect_flag == 1)
    {
        char call_id[6];
        snprintf(call_id, sizeof(call_id), "%05d", rand() % 100000);
        
        time_t timestamp = RTC_getTimestamp();

        cJSON* root = cJSON_CreateObject();
        cJSON* data = cJSON_CreateObject();

        // 填充JSON数据
        cJSON_AddStringToObject(root, "id", (const char*)bt_get_current_info()->BT_Add);
        cJSON_AddStringToObject(root, "callId", call_id);
        cJSON_AddStringToObject(root, "type", "data");
        cJSON_AddNumberToObject(data, "times", timestamp);
        cJSON_AddNumberToObject(data, "steps", sensor_data->steps);
        cJSON_AddNumberToObject(data, "heart", sensor_data->heart_rate);
        cJSON_AddNumberToObject(data, "blood", sensor_data->blood_oxygen);
        cJSON_AddItemToObject(root, "data", data);

        char* request = cJSON_PrintUnformatted(root);

        if(request == NULL)
        {
            printf("request is NULL! cJSON_Print failed!\r\n");
            return 0;
        }
        else
        {
            printf("request:%s request_len:%d\r\n", request, strlen(request));
            char full_requestdata[256] = {0};
            sprintf(full_requestdata, "@D@%s@E@\r\n", request);
            printf("full_requestdata:%s full_requestdata_len:%d\r\n", full_requestdata, strlen(full_requestdata));
            send_ble_data((uint8_t*)full_requestdata, DATA, call_id, NULL);
            cJSON_free(request);
        }
        cJSON_Delete(root);

        return 1;
    }

    return 0;
}

/**
  * @brief 发送响应
  * @param code 响应代码
  * @param msg 响应消息
  * @param callId 调用ID
  * @retval None
  */
static void send_response(uint16_t code, char* msg, char* callId)
{
	time_t timestamp = RTC_getTimestamp();
    
    cJSON* response = cJSON_CreateObject();
    cJSON_AddStringToObject(response, "id", (const char*)bt_get_current_info()->BT_Add);
    cJSON_AddStringToObject(response, "callId", callId);
    cJSON_AddNumberToObject(response, "code", code);
    cJSON_AddStringToObject(response, "msg", msg);
    cJSON_AddNumberToObject(response, "times", timestamp);

    char* response_str = cJSON_PrintUnformatted(response);
    if(response_str != NULL)
    {
        char full_response[256];
        snprintf(full_response, sizeof(full_response), "@R@%s@E@\r\n", response_str);
        send_ble_data((uint8_t*)full_response, RES, NULL, NULL);
        cJSON_free(response_str);
    }
    cJSON_Delete(response);
}

/**
  * @brief 处理BT+DC命令（断开连接）
  * @param id 主机ID
  * @param callId 调用ID
  * @param HostId 蓝牙主机ID
  * @retval None
  */
static void handle_cmd_dc(const cJSON* id, const cJSON* callId, uint8_t * HostId)
{
    send_response(200, "successful", callId->valuestring);  // 发送成功响应
    bt_info* info = bt_get_current_info();
    info->connect_flag = 0;  // 设置连接标志为0
    memset(info->hostId, 0, strlen((const char*)info->hostId));  // 清空主机ID
}

/**
  * @brief 处理BT+HS命令（握手）
  * @param id 主机ID
  * @param callId 调用ID
  * @param HostId 蓝牙主机ID
  * @retval None
  */
static void handle_cmd_hs(const cJSON* id, const cJSON* callId, uint8_t* HostId)
{
    printf("id:%s\r\n", id->valuestring);  // 打印接收到的主机ID
    
    if (id == NULL || !cJSON_IsString(id) || id->valuestring == NULL)
    {
        send_response(400, "invalid id", callId ? callId->valuestring : "");
        return;  // 如果ID无效，返回400错误
    }

    bt_info* info = bt_get_current_info();
    info->connect_flag = 1;  // 设置连接标志为1

    strncpy((char*)info->hostId, id->valuestring, sizeof(info->hostId) - 1);  // 拷贝主机ID
    info->hostId[sizeof(info->hostId) - 1] = '\0';  // 确保ID以'\0'结尾

    send_response(200, "successful", callId->valuestring);  // 发送成功响应
}

static const char* commands[] =
{
    "BT+DC",
    "BT+HS",
};
typedef void (*cmd_handler_t)(const cJSON* id, const cJSON* callId,uint8_t * HostId);
cmd_handler_t command_handlers[] =
{
    handle_cmd_dc,
    handle_cmd_hs,
};

/**
  * @brief 处理蓝牙命令并调用相应的处理函数
  * @param command 接收到的命令数据
  * @retval 若命令处理成功，返回1；否则返回0
  */
_Bool handle_ble_command(uint8_t* command)
{
    char* start = strstr((const char*)command, "@C@");  // 查找命令开始标志
    char* end = strstr((const char*)command, "@E@");  // 查找命令结束标志

    if (!start || !end || start >= end)
    {
        return 0;  // 如果命令格式无效，返回0
    }

    start += 3;  // 跳过 "@C@"

    size_t json_len = end - start;  // 计算JSON字符串长度
    char json_str[256] = {0};
    strncpy(json_str, start, json_len);  // 拷贝命令中的JSON部分
    json_str[json_len] = '\0';  // 添加字符串结束符

    cJSON* root = cJSON_Parse(json_str);  // 解析JSON数据
    if(root == NULL)
    {
        return 0;  // 如果解析失败，返回0
    }

    cJSON* com = cJSON_GetObjectItem(root, "com");
    cJSON* id = cJSON_GetObjectItem(root, "id");
    cJSON* callId = cJSON_GetObjectItem(root, "callId");

    if (!cJSON_IsString(id) || !cJSON_IsString(callId) || !cJSON_IsString(com))
    {
        cJSON_Delete(root);
        return 0;  // 如果命令字段无效，返回0
    }

    bt_info* current_info = bt_get_current_info();

    // 查找并执行相应的命令处理函数
    for(int i = 0; i < sizeof(commands) / sizeof(commands[0]); ++i)
    {
        if(strcmp(com->valuestring, commands[i]) == 0)
        {
            command_handlers[i](id, callId, current_info->hostId);  // 调用相应的命令处理函数
            cJSON_Delete(root);
            return 1;
        }
    }

    cJSON_Delete(root);
    return 0;  // 如果没有找到匹配的命令，返回0
}

/**
  * @brief 发送命令到蓝牙设备
  * @param com 发送的命令类型
  * @retval 返回1表示成功，0表示失败
  */
_Bool SendCommand(sendCom com)
{
//	printf("Send Com:%s\r\n", send_command_map[com].command_str);
	char call_id[6];
	snprintf(call_id, sizeof(call_id), "%05d", rand() % 100000);  // 生成随机的callId
	call_id[5] = '\0';

	time_t timestamp = RTC_getTimestamp();  // 获取当前时间戳

	cJSON* command = cJSON_CreateObject();
	cJSON_AddStringToObject(command, "id", (const char*)bt_get_current_info()->BT_Add);
	cJSON_AddStringToObject(command, "callId", call_id);
	cJSON_AddStringToObject(command, "type", "command");
	cJSON_AddStringToObject(command, "com", send_command_map[com].command_str);
	cJSON_AddNumberToObject(command, "times", timestamp);

	char* request = cJSON_PrintUnformatted(command);

	if(request == NULL)
	{
		cJSON_Delete(command);
		return 0;  // 如果生成请求失败，返回0
	}
	else
	{
		char full_requestdata[256];
		snprintf(full_requestdata, sizeof(full_requestdata), "@C@%s@E@", request);  // 构建完整请求数据
		send_cmd_handler_t handler = send_command_map[com].handler;  // 获取命令对应的处理函数
		send_ble_data((uint8_t*)full_requestdata, COM, call_id, handler);  // 发送命令
		cJSON_free(request);
	}
	cJSON_Delete(command);

	return 1;
}

/**
  * @brief 断开当前蓝牙连接
  * @retval 返回1表示断开成功，返回0表示断开失败或未连接
  */
_Bool BT_Disconnect()
{
    bt_info* current_info = bt_get_current_info();  // 获取当前蓝牙连接信息
    if(current_info->connect_flag == 1)  // 如果当前处于连接状态
    {
        uint8_t retry = 3;  // 最多重试3次
        char call_id[6];
        snprintf(call_id, sizeof(call_id), "%05d", rand() % 100000);  // 生成随机的 callId
        call_id[5] = '\0';

        time_t timestamp = RTC_getTimestamp();  // 获取当前 RTC 时间戳（秒级）

        // 构造 JSON 命令结构
        cJSON* command = cJSON_CreateObject();
        cJSON_AddStringToObject(command, "id", (const char*)current_info->BT_Add);  // 当前设备蓝牙地址
        cJSON_AddStringToObject(command, "callId", call_id);                         // 唯一标识本次请求
        cJSON_AddStringToObject(command, "type", "command");                         // 类型为 command
        cJSON_AddStringToObject(command, "com", send_command_map[BT_DC].command_str);      // 命令为蓝牙断开（BT+DC）
        cJSON_AddNumberToObject(command, "times", timestamp);                        // 加入时间戳字段

        // 将 cJSON 转为无格式字符串
        char* request = cJSON_PrintUnformatted(command);
        if(request == NULL)
        {
            cJSON_Delete(command);  // 清理 JSON 对象
            return 0;  // 如果生成失败则返回 0
        }
        else
        {
            cJSON_Delete(command);  // 构造完成后删除 JSON 对象（请求字符串已生成）

            // 构造完整数据包，格式为 "@C@<数据>@E@" 以供传输
            char full_requestdata[256];
            snprintf(full_requestdata, sizeof(full_requestdata), "@C@%s@E@", request);

            if(send_ble_data((uint8_t*)full_requestdata, COM, call_id, NULL))  // 发送命令
            	return 1;

            // 如果重试结束仍未断开成功
            cJSON_free(request);
            return 0;
        }
    }
    else
    {
        return 0;  // 当前未连接
    }
}
